#include "math.h"
#include "ManejadorBits.h"
#include "funciones.h"

namespace Toolkit
{

ManejadorBits::ManejadorBits()
{
	reset();
}

ManejadorBits::~ManejadorBits()
{
}

void ManejadorBits::procesar(unsigned int &techo, unsigned int &piso)
{
	if (estaOverflow(techo, piso))
	{
		manejarOverflow(techo, piso);
	}

	if ( contadorOverflow != 0)
	{
		ufEmisionesVacias = 0;
	} else {
		ufEmisionesVacias = contadorUnderflow;
	}

	/* luego de que se maneje el overflow, puede quedar en underflow */
	if (estaUnderflow(techo, piso))
	{
		manejarUnderflow(techo, piso);
	}
}

bool ManejadorBits::estaOverflow(const unsigned int &techo, const unsigned int &piso) const
{
	int primerBitTecho = obtenerBit(techo, 0);
	int primerBitPiso  = obtenerBit(piso, 0);

	return (primerBitTecho == primerBitPiso);
}

bool ManejadorBits::estaUnderflow(const unsigned int &techo, const unsigned int &piso) const
{
	int primerBitTecho = obtenerBit(techo, 0);
	int primerBitPiso  = obtenerBit(piso, 0);
	int segundoBitTecho = obtenerBit(techo, 1);
	int segundoBitPiso  = obtenerBit(piso, 1);

	return ((primerBitTecho == 1) && (primerBitPiso == 0) &&
		    (segundoBitTecho == 0) && (segundoBitPiso == 1));
}

void ManejadorBits::manejarOverflow(unsigned int &techo, unsigned int &piso)
{
	contadorOverflow = 0;
	const unsigned int tamanoInt = sizeof(int) * 8;

	for(unsigned int i = 0; i < tamanoInt; i ++)
	{
		if (obtenerBit(techo, i) != obtenerBit(piso, i))
		{
			break;
		}

		contadorOverflow++;
	}

	emitir(techo, contadorOverflow);

	techo = techo << contadorOverflow;
	piso = piso << contadorOverflow;

	unsigned int posicionInicial = tamanoInt - contadorOverflow;

	negarRangoBits(techo, posicionInicial, tamanoInt);
}

void ManejadorBits::emitir(const unsigned int &numero, const unsigned int &cantidadOverflow)
{
	unsigned int bitMasSignificativo = obtenerBit(numero, 0);
	unsigned int bitNegado = bitMasSignificativo ^ 1;

	emision += bitMasSignificativo;

	while (contadorUnderflow > 0)
	{
		emision += bitNegado;
		contadorUnderflow--;
	}

	for (unsigned int i = 1; i < cantidadOverflow; i++)
	{
		emision += obtenerBit(numero, i);
	}
}

void ManejadorBits::manejarUnderflow(unsigned int &techo, unsigned int &piso)
{
	const unsigned int tamanoInt = sizeof(int) * 8;

	unsigned int cantidadUnderflow = 0;
	for(unsigned int i = 1; i < tamanoInt; i ++)
	{
		if ((obtenerBit(techo, i) != 0) || (obtenerBit(piso, i) != 1))
		{
			break;
		}

		cantidadUnderflow++;
	}

	this->contadorUnderflow += cantidadUnderflow;

	piso = piso << (cantidadUnderflow + 1);
	piso = piso >> 1;

	techo = techo << (cantidadUnderflow + 1);
	techo = techo >> 1;

	negarRangoBits(techo, 0, 1);
	negarRangoBits(techo, tamanoInt - cantidadUnderflow, tamanoInt);
}

void ManejadorBits::agregarPadding()
{
	int tamanoPadding = 8 - emision.size() % 8;
	emision += 1;
	tamanoPadding--;

	int cero = 0;

	while (tamanoPadding > 0)
	{
		emision += cero;
		tamanoPadding--;
	}
}

string ManejadorBits::getEmision()
{
	agregarPadding();
	const int tamanoChar = sizeof(char) * 8;
	int cantidadCaracteres = emision.size() / tamanoChar;
	string emisionResultado = "";

	int posicion = 0;

	for (int i = 0; i < cantidadCaracteres; i++)
	{
		int bufferBits = 0;

		for (int j = posicion; j < (posicion + tamanoChar); j++)
		{
			bufferBits = bufferBits << 1;
			bufferBits += emision[j];
		}

		emisionResultado += bufferBits;
		posicion += tamanoChar;
	}

	return emisionResultado;
}

unsigned int ManejadorBits::compactarVentana(unsigned int &ventana)
{
	ventana = ventana << contadorOverflow;

	int bitMasSignificativo = obtenerBit(ventana, 0);

	ventana = ventana << (contadorUnderflow - ufEmisionesVacias + 1);
	ventana = ventana >> 1;

	if (bitMasSignificativo == 1)
	{
		negarRangoBits(ventana, 0, 1);
	}

	unsigned int resultado = contadorOverflow + contadorUnderflow - ufEmisionesVacias;

	contadorOverflow = 0;
	return resultado;
}

void ManejadorBits::negarRangoBits(unsigned int &numero, const unsigned int &posicionInicial,
							  const unsigned int &posicionFinal) const
{
	for (unsigned int i = posicionInicial; i < posicionFinal; i ++)
	{
		numero = numero ^ crearMascara(i);
	}
}

void ManejadorBits::reset()
{
	this->contadorUnderflow = 0;
	this->contadorOverflow = 0;
	this->ufEmisionesVacias = 0;
	this->emision = "";
}

}
